package com.example.demo.aspect_;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.api.R;
import com.example.demo.config.DataSourceContextHolder;
import com.example.demo.constant.RedisConstant;
import com.example.demo.service.ChangeDataSourceService;
import com.example.demo.util.Redis2Service;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * fileName:DataSourceAspect
 * description: 数据源切面
 * author: LJV
 * createTime:2022/4/6 13:55
 * version:1.0.0
 */
@Aspect
@Component
@Lazy(false)
@Slf4j
public class DataSourceAspect {

    @Autowired
    private ChangeDataSourceService changeDataSourceService;

    @Autowired
    private Redis2Service redis2Service;

    //注解方式实现切面
//    @Pointcut("@annotation(com.example.demo.aspect_.DataSourceAnnotation)")
    //匹配方式实现切面
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    private void cutMethod() {}

    /**
     * 前置通知：在目标方法执行前调用
     */
    @Before("cutMethod()")
    public void begin(JoinPoint joinPoint) {
        /*Object[] args = joinPoint.getArgs();
        Arrays.stream(args).forEach(a ->{
            System.err.println("参数类型："+a.getClass().getName()+"，值："+a);
        });*/
    }

    /**
     * 后置通知：在目标方法执行后调用，若目标方法出现异常，则不执行
     */
    @AfterReturning("cutMethod()")
    public void afterReturning(JoinPoint joinPoint) {
    }

    /**
     * 后置/最终通知：无论目标方法在执行过程中是否出现异常都会在它之后调用
     */
    @After("cutMethod()")
    public void after() {
    }

    /**
     * 异常通知：目标方法抛出异常时执行
     */
    @AfterThrowing(pointcut = "cutMethod()",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Exception ex) {
    }

    /**
     * 环绕通知：灵活自由的在目标方法中切入代码
     */
    @Around("cutMethod()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 执行源方法
        Object result = null;
        //切换数据源
        String datasourceId = DataSourceContextHolder.getDataSource();
        changeDataSourceService.changeDS(datasourceId);
        System.out.println("环绕通知：在目标方法之前切换数据源。输出时间：" + LocalDateTime.now());
        //1.目标方法调用
        result = joinPoint.proceed();//method.invoke(); Object result = doFirst();
        //切回主数据源
        DataSourceContextHolder.removeDataSource();
        System.out.println("环绕通知：在目标方法之后，切回数据源");
        return result;
    }

    /**
     * 获取方法中声明的注解
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    /*public DataSourceAnnotation getDeclaredAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        // 反射获取目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型（重载）获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        // 拿到方法定义的注解信息
        DataSourceAnnotation annotation = objMethod.getDeclaredAnnotation(DataSourceAnnotation.class);
        // 返回
        return annotation;
    }*/

}
